home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 07 - 1991 / MacTutor Live! SF 91 / GIFfy / GIF.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-01-07  |  10.9 KB  |  413 lines  |  [TEXT/MPS ]

  1. #include    <types.h>
  2. #include    <memory.h>
  3. #include    <windows.h>
  4. #include    <toolutils.h>
  5. #include    <quickdraw.h>
  6. #include    <palette.h>
  7. #include    <resources.h>
  8. #include    <files.h>
  9. #include    "GIF.h"
  10.  
  11. /*
  12.  *    The following three pointers all start out pointing at the same thing,
  13.  *    the start of the data read in; however, thePtr is used as a pointer into
  14.  *    the data for the byte we're currently processing and image is used for
  15.  *    the raster image being generated.
  16.  */
  17. Ptr    thePtr;
  18. Ptr    startOfData;    /* Kept as a pointer to the block for when we dispose of it */
  19. Ptr    image;
  20.  
  21. long    colorMapSize;
  22. long    mask;
  23. long    vaxMask;
  24. long    codeSize;    /* Really only need a short, but this makes the shifts more efficient */
  25. long    bitsIn = 0;
  26. long    outCount = 0;
  27. long    passnum = 0;
  28. short    xCoord = 0;
  29. short    yCoord = 0;
  30. Boolean    isInterlaced;
  31. short    prefix[4096];
  32. short    suffix[4096];
  33. short    output[1025];
  34.  
  35. PaletteHandle    thePalette = NIL;
  36. CWindowPtr        palWindow;
  37. CWindowPtr        gifWindow;
  38. long            offRowBytes;
  39. Ptr                aBitMap;
  40. ImageDescriptor    theDesc;
  41.  
  42. typedef    BitMap    *BitMapPtr;
  43.  
  44. /*
  45.  *    PROTOTYPES
  46.  */
  47. long GetCode();
  48. void AdjustPixMap(short index);
  49.  
  50.  
  51. Boolean    ProcessGIF(Str255 filename, short vRefnum)
  52. {
  53.     short            refnum;        /* file reference number */
  54.     register Ptr    thePtr;        /* Ptr into the unparsed data stream */
  55.     register long    i;            /* used for loops */
  56.     long            temp;
  57.     ScreenDescriptor    theScrDesc;
  58.     RGBColor        theRGB;
  59.     long            filelength;
  60.     Rect            r1;
  61.     Rect            r2;
  62.     Rect            gRect;
  63.     Rect            gifRect;
  64.     Rect            palRect;
  65.     Boolean            hasColorMap;
  66.     long            pixelDepth;
  67.     long            row;
  68.     long            col;
  69.     Point            pt;
  70.     GDHandle        theMaxDevice;
  71.     GDHandle        saveDevice;
  72.     CTabHandle        theCMHandle;
  73.     CGrafPort        aCGrafPort;
  74.     CGrafPtr        aCGrafPtr;
  75.     short            depth;
  76.     long            sizeOfOffscreen;
  77.     Ptr                ptr;
  78.     short            c, c1;
  79.     
  80.     /*
  81.      *    LZW "holders" - they're longs for convenience of bit operations (conceptually, they're
  82.      *    shorts.
  83.      */
  84.     long            current;
  85.     long            final;
  86.     long            old;
  87.     long            input;
  88.     long            free, firstFree, resetCode, eof, code, initialCodeSize, maxCode;
  89.     
  90.     /*    Open the file and get the data */
  91.     if (FSOpen(filename, vRefnum, &refnum) != noErr) return(FALSE);
  92.     
  93.     if (GetEOF(refnum, &filelength) != noErr) {
  94.         FSClose(refnum);
  95.         return(FALSE);
  96.     }
  97.     
  98.     if ((thePtr = startOfData = image = NewPtr(filelength)) == NIL) {
  99.         FSClose(refnum);
  100.         return(FALSE);
  101.     }
  102.     
  103.     if (FSRead(refnum, &filelength, startOfData) != noErr) {
  104.         DisposPtr(startOfData);
  105.         FSClose(refnum);
  106.         return(FALSE);
  107.     }
  108.     
  109.     FSClose(refnum);
  110.     
  111.     
  112.     /* (Re)initialize internal variables */
  113.     bitsIn = outCount = passnum = yCoord = xCoord = 0;
  114.     
  115.     /* Check the file signature */
  116.     signature[0] = 6;    /* Length of the "string" (Pascal-style) */
  117.     for (i = 1; i <= 6; i++) {
  118.         signature[i] = *thePtr++;
  119.     }
  120.     if (!EqualString(signature, "\pGIF87a", TRUE, TRUE)) {
  121.         DisposPtr(startOfData);
  122.         return(FALSE);
  123.     }
  124.     
  125.     /* Parse the header (we ignore some fields) --
  126.        Numeric values are stored in VAX (or Intel) format, least significant
  127.        byte first.  Therefore, whenever we have to bring in an integer, we
  128.        need to bring it in a byte at a time and reverse the order.  This is
  129.        achieved by masking off the low order byte of the derefed pointer (to
  130.        ignore garbage in the high-order portion of the register to which it
  131.        is moved) and adding that value to the following byte, similarly masked
  132.        and shifted left by 8 bits */
  133.     theScrDesc.rastWidth = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
  134.     theScrDesc.rastHeightLo = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
  135.     
  136.     temp = theScrDesc.clrFlags = ((*thePtr++) & 0xFF);
  137.     theScrDesc.background = ((*thePtr++) & 0xFF);
  138.     theScrDesc.terminator = ((*thePtr++) & 0xFF);
  139.     /* Parse the flags */
  140.     hasColorMap = ( (temp & colorMapMask) ? TRUE : FALSE);
  141.     pixelDepth = (temp & pixelMask) + 1;
  142.     colorMapSize = 1 << pixelDepth;
  143.     mask = colorMapSize - 1;
  144.     
  145.     if (hasColorMap && ((thePalette = NewPalette(colorMapSize, NIL, pmTolerant, 0)) != NIL)) {
  146.         for (i = 0; i < colorMapSize; i++) {
  147.             /* GIF color intensities are in the range 0..255, Mac RGB values are
  148.                in the range 0..65536.  We convert these by mapping the 8-bit values
  149.                to a 16-bit value where each byte of the 16-bit value matches the
  150.                original 8-bit value.  We can do this by multiplying by 256 or
  151.                adding the 8-bit value to itself, shifted left 8-bits.  The shift/add
  152.                is faster, but the multiply is easier to read.
  153.             */
  154.             temp = ((*thePtr++) & 0xFF);
  155.             theRGB.red = temp * 257;
  156.             temp = ((*thePtr++) & 0xFF);
  157.             theRGB.green = temp * 257;
  158.             temp = ((*thePtr++) & 0xFF);
  159.             theRGB.blue = temp * 257;
  160.             SetEntryColor(thePalette, i, &theRGB);
  161.         }
  162.     }
  163.     
  164.     if (((*thePtr++) & 0xFF) != IMAGESEPARATOR) { /* sanity check */
  165.         if (thePalette) {
  166.             DisposePalette(thePalette);
  167.         }
  168.         DisposPtr(startOfData);
  169.         return(FALSE);
  170.     }
  171.     
  172.     theDesc.leftInset = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
  173.     theDesc.vertInset = ((*thePtr++) & 0xFF) + (((*thePtr++) & 0xFF) << 8);
  174.     theDesc.imageWidth = ((*thePtr++) & 0xFF);
  175.     theDesc.imageWidth += (((*thePtr++) & 0xFF) << 8);
  176.     theDesc.imageHeight = ((*thePtr++) & 0xFF);
  177.     theDesc.imageHeight += (((*thePtr++) & 0xFF) << 8);
  178.     theDesc.imageFlags = ((*thePtr++) & 0xFF);
  179.     isInterlaced = ( (theDesc.imageFlags & interlaceMask) ? TRUE : FALSE);
  180.     
  181.     /*    Create and display the "Palette" Window 4 pixels in from the left of
  182.         the main screen, centered vertically */
  183.     palRect.top = palRect.left = 0;
  184.     palRect.right = palRect.bottom = 1 + (PALENTSIZE << 4);
  185.     OffsetRect(&palRect, 4, (qd.screenBits.bounds.bottom - palRect.bottom) / 2);
  186.     if ((palWindow = (CWindowPtr)NewCWindow(NIL, &palRect, '', TRUE, 3, BEHIND, FALSE, 0L)) == NIL) {
  187.         if (thePalette) {
  188.             DisposePalette(thePalette);
  189.         }
  190.         DisposPtr(startOfData);
  191.         return(FALSE);
  192.     }
  193.     SetPort((WindowPtr) palWindow);
  194.     if (thePalette) {
  195.         SetPalette((WindowPtr) palWindow, thePalette, TRUE);
  196.         ActivatePalette((WindowPtr) palWindow);
  197.     }
  198.     r1 = palWindow->portRect;
  199.     for (i = 0; i < colorMapSize; i++) {
  200.         /* allow 16 rows of 16 columns */
  201.         PmForeColor(i);
  202.         row = (i / 16) * ((r1.bottom-r1.top) / 16) + 1;
  203.         col = (i % 16) * ((r1.right-r1.left) / 16) + 1;
  204.         r2.top = row;
  205.         r2.left = col;
  206.         r2.bottom = row + ((r1.right - r1.left) / 16) - 1;
  207.         r2.right = col + ((r1.right - r1.left) / 16) - 1;
  208.         PaintRect(&r2);
  209.     }
  210.     
  211.     SetCursor(*GetCursor(watchCursor));
  212.     
  213.     /* Create the window in which the GIF will be displayed.  A real application
  214.        would be nicer about positioning the window and would probably make it
  215.        a document window so that the user could drag it around. */
  216.     SetRect(&gifRect, 0, 0, theDesc.imageWidth, theDesc.imageHeight);
  217.     r2 = gifRect;
  218.     OffsetRect(&gifRect, 10, 30);
  219.     if ((gifWindow = (CWindowPtr)NewCWindow(NIL, &gifRect, '', TRUE, 3, BEHIND, FALSE, 0L)) == NIL) {
  220.         if (thePalette) {
  221.             DisposePalette(thePalette);
  222.         }
  223.         DisposPtr(startOfData);
  224.         DisposeWindow((WindowPtr) palWindow);
  225.         return(FALSE);
  226.     }
  227.     SetPort((WindowPtr) gifWindow);
  228.     if (thePalette != NIL) {
  229.         SetPalette((WindowPtr) gifWindow, thePalette, TRUE);
  230.         ActivatePalette((WindowPtr) gifWindow);
  231.     }
  232.     
  233.     gRect = r2;
  234.     pt.v = gRect.top;
  235.     pt.h = gRect.left;
  236.     LocalToGlobal(&pt);
  237.     gRect.top = pt.v;
  238.     gRect.left = pt.h;
  239.     pt.v = gRect.bottom;
  240.     pt.h = gRect.right;
  241.     LocalToGlobal(&pt);
  242.     gRect.bottom = pt.v;
  243.     gRect.right = pt.h;
  244.     
  245.     theMaxDevice = GetMaxDevice(&gRect);
  246.     saveDevice = GetGDevice();
  247.     SetGDevice(theMaxDevice);
  248.     aCGrafPtr = &aCGrafPort;
  249.     OpenCPort(aCGrafPtr);
  250.     depth = (**(*aCGrafPtr).portPixMap).pixelSize;
  251.     offRowBytes = (((depth * theDesc.imageWidth) + 15) >> 4) << 1;
  252.     sizeOfOffscreen = (long) theDesc.imageHeight * offRowBytes;
  253.     aBitMap = NewPtr(sizeOfOffscreen);
  254.     (**(*aCGrafPtr).portPixMap).baseAddr = aBitMap;
  255.     (**(*aCGrafPtr).portPixMap).rowBytes = offRowBytes + 0x8000;
  256.     (**(*aCGrafPtr).portPixMap).bounds = r2;
  257.     theCMHandle = (**(**theMaxDevice).gdPMap).pmTable;
  258.     /* Need to make a copy of the color table from the palette mgr */
  259.     if (HandToHand(&(Handle)theCMHandle) != noErr) {
  260.         CloseCPort(aCGrafPtr);
  261.         SetGDevice(saveDevice);
  262.         if (thePalette) {
  263.             DisposePalette(thePalette);
  264.         }
  265.         DisposPtr(startOfData);
  266.         return(FALSE);
  267.     }
  268.     for (i = 0; i <= (**theCMHandle).ctSize; ++i) {
  269.         (**theCMHandle).ctTable[i].value = i;
  270.     }
  271.     (**theCMHandle).ctFlags &= MAXSHORT;
  272.     (**theCMHandle).ctSeed = GetCTSeed();        /* Safe, GetCTSeed doesn't move memory */
  273.     if (thePalette) {
  274.         Palette2CTab(thePalette, theCMHandle);
  275.     }
  276.     (**(*aCGrafPtr).portPixMap).pmTable = theCMHandle;
  277.     SetPort((WindowPtr) aCGrafPtr);
  278.     
  279.     /* Now let's start decompressing the LZW data */
  280.     /* First we initialize the codetable variables */
  281.     codeSize = ((*thePtr++) & 0xFF);
  282.     resetCode = (1 << codeSize);
  283.     eof = resetCode + 1;
  284.     free = firstFree = resetCode + 2;
  285.     
  286.     codeSize++;
  287.     initialCodeSize = codeSize;
  288.     maxCode = (1 << codeSize);
  289.     vaxMask = maxCode - 1;
  290.     /*
  291.      * Move the block forward in our buffer — this overwrites the header info, etc that
  292.      * we have already processed -- end movement when "0" byte encountered.
  293.      */ 
  294.     ptr = image;
  295.     do {
  296.         c = c1 = ((*thePtr++) & 0xFF);
  297.         while (c--) {
  298.             *ptr++ = ((*thePtr++) & 0xFF);
  299.         }
  300.     } while (c1);
  301.     
  302.     code = GetCode();
  303.     while (code != eof) {
  304.         if (code == resetCode) {
  305.             codeSize = initialCodeSize;
  306.             maxCode = (1 << codeSize);
  307.             vaxMask = maxCode - 1;
  308.             free = firstFree;
  309.             current = old = code = GetCode();
  310.             final = current & mask;
  311.             AdjustPixMap(final);
  312.         } else {
  313.             current = input = code;
  314.             if (current >= free) {
  315.                 current = old;
  316.                 output[outCount++] = suffix[current];
  317.             }
  318.             while (current > mask) {
  319.                 output[outCount++] = suffix[current];
  320.                 current = prefix[current];
  321.             }
  322.             final = current & mask;
  323.             output[outCount++] = final;
  324.             for (i = outCount-1; i>= 0; i--) {
  325.                 AdjustPixMap(output[i]);
  326.             }
  327.             outCount = 0;
  328.             prefix[free] = old;
  329.             suffix[free] = final;
  330.             old = input;
  331.             free++;
  332.             if (free >= maxCode) {
  333.                 if (codeSize < 12) {
  334.                     codeSize++;
  335.                     maxCode *= 2;
  336.                     vaxMask = (1 << codeSize) - 1;
  337.                 }
  338.             }
  339.         }
  340.         code = GetCode();
  341.     }
  342.     
  343.     /* Done decompressing, clean up after ourselves */
  344.     DisposPtr(startOfData);
  345.     SetPort((WindowPtr) gifWindow);
  346.     SetGDevice(saveDevice);
  347.     CopyBits( (BitMapPtr) *(*aCGrafPtr).portPixMap,
  348.               (BitMapPtr) *(*gifWindow).portPixMap, &r2, &r2, 0, 0L);
  349.     CloseCPort(aCGrafPtr);
  350.     DisposPtr(aBitMap);
  351.     DisposHandle((Handle) theCMHandle);
  352.     return(TRUE);
  353. }
  354.  
  355. long GetCode()
  356. {
  357.     long    aCode, index;
  358.     
  359.     index = bitsIn / 8;
  360.     aCode = (image[index] & 0xFF) + ((image[index+1] & 0xFF) << 8);
  361.     if (codeSize >= 8) {
  362.         aCode += ((image[index+2] & 0xFF) << 16);
  363.     }
  364.     aCode >>= (bitsIn % 8);
  365.     bitsIn += codeSize;
  366.     return(aCode & vaxMask);
  367. }
  368.  
  369. void AdjustPixMap(short index)
  370. {
  371.     aBitMap[yCoord*offRowBytes + xCoord] = (index & 0xFF);
  372.     
  373.     if (++xCoord == theDesc.imageWidth) {
  374.         xCoord = 0;
  375.         if (!isInterlaced) {
  376.             yCoord++;
  377.         } else {
  378.             switch (passnum) {
  379.                 case 0:
  380.                     yCoord += 8;
  381.                     if (yCoord >= theDesc.imageHeight) {
  382.                         passnum++;
  383.                         yCoord = 4;
  384.                     }
  385.                     break;
  386.                 
  387.                 case 1:
  388.                     yCoord += 8;
  389.                     if (yCoord >= theDesc.imageHeight) {
  390.                         passnum++;
  391.                         yCoord = 2;
  392.                     }
  393.                     break;
  394.                 
  395.                 case 2:
  396.                     yCoord += 4;
  397.                     if (yCoord >= theDesc.imageHeight) {
  398.                         passnum++;
  399.                         yCoord = 1;
  400.                     }
  401.                     break;
  402.                 
  403.                 case 3:
  404.                     yCoord += 2;
  405.                     break;
  406.                 
  407.                 default:
  408.                     break;
  409.             }
  410.         }
  411.     }
  412. }
  413.